package 多线程;

import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;

/**
 * 该类用于实现A、B、C三个字母的交替打印。
 * 使用ReentrantLock和Condition实现线程间的同步，确保按顺序打印。
 * 时间复杂度：O(n)，空间复杂度：O(1)
 */
public class PrintABCUsingLockCondition {
    private int times; // 打印次数
    private int state; // 当前状态
    private static Lock lock = new ReentrantLock(); // 可重入锁
    private static Condition c1 = lock.newCondition(); // 条件变量1
    private static Condition c2 = lock.newCondition(); // 条件变量2
    private static Condition c3 = lock.newCondition(); // 条件变量3

    /**
     * 构造函数，初始化打印次数。
     * @param times 打印次数
     */
    public PrintABCUsingLockCondition(int times) {
        this.times = times;
    }

    /**
     * 打印字母的方法。
     * 使用条件变量确保按顺序打印A、B、C。
     * @param name 要打印的字母
     * @param targetNum 目标状态
     * @param current 当前条件变量
     * @param next 下一个条件变量
     */
    private void printLetter(String name, int targetNum, Condition current, Condition next) {
        for(int i = 0; i < times; ) {
            lock.lock(); // 获取锁
            try {
                while(state % 3 != targetNum) {
                    current.await(); // 等待条件满足
                }
                state++; // 更新状态
                i++; // 打印次数加1
                System.out.println(name); // 打印字母
                next.signal(); // 通知下一个线程
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock(); // 释放锁
            }
        }
    }

    /**
     * 主方法，启动三个线程分别打印A、B、C。
     * @param args 命令行参数
     */
    public static void main(String[] args) {
        PrintABCUsingLockCondition print = new PrintABCUsingLockCondition(3);
        new Thread(() -> {
            print.printLetter("a", 0, c1, c2);
        }, "a").start(); // 启动打印A的线程
        new Thread(() -> {
            print.printLetter("b", 1, c2, c3);
        }, "b").start(); // 启动打印B的线程
        new Thread(() -> {
            print.printLetter("c", 2, c3, c1);
        }, "c").start(); // 启动打印C的线程
    }
}
